package xin.shaozeming.base.config.controller;


import lombok.extern.log4j.Log4j2;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.core.MethodParameter;
import org.springframework.http.*;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import xin.shaozeming.base.common.Enum.base.State;
import xin.shaozeming.base.common.Response;
import xin.shaozeming.base.common.aop.annotation.ParamsDecrypted;
import xin.shaozeming.base.common.ex.CustomerException;
import xin.shaozeming.base.common.utils.DecryptUtil;


import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;

import static org.springframework.http.HttpStatus.NOT_EXTENDED;

/**
 * @author: 邵泽铭
 * @date: 2019/4/17
 * @description:
 **/
@Log4j2
@ControllerAdvice
public class GlobalControllerHandler extends ResponseEntityExceptionHandler implements RequestBodyAdvice {

    @InitBinder
    protected void initBinder(HttpServletRequest request,ServletRequestDataBinder binder) throws Exception {
        binder.registerCustomEditor(
                Date.class,
                new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"),true));
        binder.registerCustomEditor(
                Date.class,
                new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true));
    }


        /**
         * 在controller里面内容执行之前，校验一些参数不匹配啊，Get post方法不对啊之类的
         */
    @Override
    protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        System.out.println("参数错误");
        return new ResponseEntity<Object>(State.RES_PARAMERROR.getvalue(), NOT_EXTENDED);
    }

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Response jsonHandler(HttpServletRequest request, Exception e) {

        if(e instanceof CustomerException){
            return new Response(State.RES_ERROR.getCode(),e.getMessage());
        }

        log.error("请求路径："+request.getRequestURL().toString()+"; 发生错误！");
        log(e, request);
        return new Response(State.RES_ERROR.getCode(),"服务器异常");
    }

    private void log(Exception ex, HttpServletRequest request) {
        log.error("************************异常开始*******************************");
        log.error("异常信息：", ex);
        log.error("请求地址：" + request.getRequestURL());
        Enumeration enumeration = request.getParameterNames();
        log.error("请求参数");
        while (enumeration.hasMoreElements()) {
            String name = enumeration.nextElement().toString();
            log.error(name + "---" + request.getParameter(name));
        }
        StackTraceElement[] error = ex.getStackTrace();
        for (StackTraceElement stackTraceElement : error) {
            log.error(stackTraceElement.toString());
        }
        log.error("************************异常结束*******************************");
    }


    @SuppressWarnings("all")
    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
       if(methodParameter.getMethodAnnotation(ParamsDecrypted.class)!=null){
         return  true;
       }
        return false;
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
        return convertBody(httpInputMessage);
    }

    @Override
    public Object afterBodyRead(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return o;
    }

    @Override
    public Object handleEmptyBody(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return o;
    }


    @SuppressWarnings("all")
   private   HttpInputMessage convertBody(HttpInputMessage httpInputMessage){

        try {
            /*获取body参数*/
            InputStream body = httpInputMessage.getBody();
            String encryptParams = IOUtils.toString(body, "UTF-8");
            /*获取密钥key并解密*/
             HttpHeaders httpHeaders= httpInputMessage.getHeaders();
            List<String> key = httpHeaders.get("key");

            if(key==null||key.size()==0){
                throw  new CustomerException("解密密钥不存在");
            }
            String params= DecryptUtil.decryptParam(key.get(0),encryptParams );

            if(params==null){
                log.error("解密失败:  key:{} encryptParams:{}", key.get(0),encryptParams);
                throw  new CustomerException("解密失败");

            }
            return  new HttpInputMessage() {
                @Override
                public HttpHeaders getHeaders() {
                    return httpInputMessage.getHeaders();
                }

                @Override
                public InputStream getBody() throws IOException {
                    return IOUtils.toInputStream(params);
                }
            };
        } catch (IOException e) {
            log.error("RequestBodyAdvice获取InputStream失败" );
        }
        return httpInputMessage;
    }


}
